package perform.database;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import perform.annotations.Column;
import perform.annotations.ID;
import perform.annotations.Indexes;
import perform.annotations.ManyToOne;
import perform.annotations.Table;
import perform.mappings.ClassMappings;
import perform.type.Type;

public class TableProcessor {
	
	private Class<? extends Type> clazz;
	private String name;
	
	private Table table;
	private IndexesProcessor indexesProcessor;
	
	private ColumnProcessor idColumn;
	private List<ColumnProcessor> columns;
	
	private boolean hasConstraints;
	private String createQuery;
	private String alterQuery;
	
	private boolean existed;
	
	public TableProcessor(Class<? extends Type> clazz){
		this.clazz = clazz;
		table = clazz.getAnnotation(Table.class);
		name = table.name();
		if(name.equals("")){
			name = clazz.getSimpleName().toLowerCase();
		}
		if(clazz.isAnnotationPresent(Indexes.class)){
			indexesProcessor = new IndexesProcessor(clazz.getAnnotation(Indexes.class));
		}
		populateColumns();
		createQuery = buildCreateQuery();
		if(hasConstraints){
			alterQuery = buildAlterQuery();
		}
	}
	
	private void populateColumns(){
		columns = new ArrayList<ColumnProcessor>();
		Map<Field, String> map = ClassMappings.getAllColumns(clazz);
		Iterator<Field> fields = map.keySet().iterator();
		while(fields.hasNext()){
			Field field = fields.next();
			columns.add(createColumn(field, map.get(field)));
		}
		columns.remove(idColumn);
	}
	private ColumnProcessor createColumn(Field field, String name){
		ColumnProcessor processor;
		if(field.isAnnotationPresent(Column.class)){
			processor = new SimpleColumnProcessor(field, name);
			if(field.isAnnotationPresent(ID.class)){
				idColumn = processor;
			}
		} else if(field.isAnnotationPresent(ManyToOne.class)){
			processor = new ManyToOneProcessor(field, name);
			hasConstraints = true;
		} else {
			processor = new OneToOneProcessor(field, name);
			hasConstraints = true;
		}
		return processor;
	}
	private String buildCreateQuery(){
		StringBuilder def = new StringBuilder("create table `").append(name).append("` (\n");
		StringBuilder index = new StringBuilder();
		def.append(idColumn.getColumnDefinition());
		index.append(idColumn.getIndexDefinition());
		for(int i = 0; i < columns.size(); i++){
			def.append(columns.get(i).getColumnDefinition());
			if(columns.get(i).isIndex()){
				index.append(columns.get(i).getIndexDefinition());
			}
		}
		if(indexesProcessor != null){
			index.append(indexesProcessor.getIndexDefinition());
		}
		def.append(index.replace(index.lastIndexOf(","), index.length(), "\n) engine = "))
				.append(table.engine().toString()).append(" ;");
		columns.add(idColumn);
		return def.toString().replaceAll("  ", " ");
	}
	private String buildAlterQuery(){
		StringBuilder builder = new StringBuilder("alter table `").append(name).append("` \n");
		for(int i = 0; i < columns.size(); i++){
			if(columns.get(i).hasConstraints()){
				builder.append(columns.get(i).getForeignKeyDefinition());
			}
		}
		return builder.replace(builder.lastIndexOf(","), builder.length(), ";").toString();
	}
	
	public Class<? extends Type> getClazz() {
		return clazz;
	}
	public String getName() {
		return name;
	}
	public ColumnProcessor getIdColumn() {
		return idColumn;
	}
	public List<ColumnProcessor> getColumns() {
		return columns;
	}
	public boolean hasConstraints() {
		return hasConstraints;
	}
	public String getCreateQuery() {
		return createQuery;
	}
	public String getAlterQuery() {
		return alterQuery;
	}
	public boolean isExisted() {
		return existed;
	}
	public void setExisted(boolean existed) {
		this.existed = existed;
	}

}
